﻿ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NeurOthello.NNetwork;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


namespace NeurOthello.Logic
{
    public class NeuroPlayer
    {
        private Network network = new Network(130, 2, new int[1] { 60 });
        public Network Network { get { return network; } }
        private Othello othello;         
        Random rand = new Random();

        public NeuroPlayer(Othello o)
        {
            othello = o;
        }

        public Position MakeTrainingMove(int playerId, double r, double learningR)
        {
            NetworkMove[] evalMoves = null;
            NetworkMove best = EvaluateAllMoves(ref evalMoves, playerId);
            /*
            List<NetworkMove> randPot = new List<NetworkMove>();

            foreach (NetworkMove move in evalMoves)
            {
                if (move.Value[playerId - 1] - move.Value[2 - playerId] + r >= best[playerId - 1] - best[2 - playerId])
                    randPot.Add(move);
            }

            NetworkMove res = randPot[rand.Next(0, randPot.Count)];
            */
            NetworkMove res = RandomExploration(evalMoves, best, r);         
           
            network.ProcessInput(othello.Inputboard, res.Value, learningR);
            
            return res;
        }

        public Position MakeMove(int playerId)
        {           
            NetworkMove[] evalMoves = null;
            NetworkMove best = EvaluateAllMoves(ref evalMoves, playerId);            

            return RandomTopExploration(evalMoves, best, playerId, 0.01);
        }

        private NetworkMove EvaluateAllMoves(ref NetworkMove[] evaluatedMoves, int playerId)
        {
            Position[] moves = othello.AllMoves(playerId);
            evaluatedMoves = new NetworkMove[moves.Length];         
            NetworkMove best = null;
            int i = 0;
            foreach (Position pos in moves)
            {
                Othello newState = new Bitboard((Bitboard)othello);
                newState.PlaceMarker(playerId, pos);               

                if (best == null)
                {                    
                    best = new NetworkMove(pos, network.ProcessInput(newState.Inputboard));
                    evaluatedMoves[i] = best;
                }
                else
                {
                    evaluatedMoves[i] = new NetworkMove(pos, network.ProcessInput(newState.Inputboard));
                    if (evaluatedMoves[i].Advantage(playerId) > best.Advantage(playerId))
                    {
                        best = evaluatedMoves[i];
                    }
                }
                i++;
            }
            return best;
        }

        private NetworkMove RandomExploration(NetworkMove[] evaluatedMoves, NetworkMove best, double r)
        {
            if (rand.NextDouble() > r)
                return best;
            else
                return evaluatedMoves[rand.Next(evaluatedMoves.Length)];
        }

        private NetworkMove RandomTopExploration(NetworkMove[] evaluatedMoves, NetworkMove best, int playerId, double r)
        {
            List<NetworkMove> top = new List<NetworkMove>();
            foreach (NetworkMove nm in evaluatedMoves)
            {
                if (nm.Advantage(playerId) + r >= best.Advantage(playerId))
                    top.Add(nm);
            }
            return top[rand.Next(top.Count)];
        }

        public void TrainPlayer(double[] result, double l)
        {
            double[] input = othello.Inputboard;
            
            network.ProcessInput(input, result, l);
        }

        public void SaveNetwork(string filename)
        {
            Stream stream = File.Open(filename, FileMode.Create);
            BinaryFormatter bformatter = new BinaryFormatter();

            Console.WriteLine("Writing Network Information");
            bformatter.Serialize(stream, network);
            stream.Close();
           // 
        }

        public void ExportNetwork(string filename)
        {
            network.Save(filename);
        }

        public void LoadNetwork(string filename)
        {
            Stream stream = File.Open(filename, FileMode.Open);
            BinaryFormatter bformatter = new BinaryFormatter();

            Console.WriteLine("Reading NN Information");
            network = (Network)bformatter.Deserialize(stream);
            stream.Close();
        }

    }
}
